package com.tqw.es.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tqw.entity.Result;
import com.tqw.es.pojo.Item;
import com.tqw.es.repository.ItemRepository;
import com.tqw.es.service.ItemService;
import com.tqw.es.vo.ItemVO;
import com.tqw.item.feign.ItemFeignClient;
import com.tqw.pojo.ItemCat;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @author Jim
 */
@Slf4j
@Service
public class ItemServiceImpl implements ItemService {
    @Autowired
    private ItemRepository itemRepository;
    @Autowired
    private ItemFeignClient itemFeignClient;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    @Qualifier("asyncExecutor")
    private ThreadPoolTaskExecutor executorService;

    /**
     * 新增商品
     * @param item 商品对象
     */
    @Override
    public void save(Item item) {
        itemRepository.save(item);
    }

    /**
     * 修改商品
     * @param item 商品对象
     */
    @Override
    public void update(Item item) {
        itemRepository.save(item);
    }

    /**
     * 删除商品
     * @param id 商品id
     */
    @Override
    public void deleteById(Integer id) {
        itemRepository.deleteById(id);
    }

    /**
     * 获取所有存在的商品数据和对应的分类先从缓存获取,没有从数据库获取并缓存
     * @return 商品数据
     */
    @Override
    public List<List<ItemVO>> getItemList() {
        ValueOperations<String, String> vo = redisTemplate.opsForValue();
        String indexCache = vo.get("indexCache");
        ObjectMapper objectMapper = new ObjectMapper();
        Map<String, List<ItemVO>> items = null;
        if (indexCache != null) {
            try {
                items = objectMapper.readValue(indexCache, new TypeReference<Map<String, List<ItemVO>>>(){});
            } catch (JsonProcessingException e) {
                log.error("com.tqw.es.service.impl.ItemServiceImpl.getItemList {} caused by redis deserialization exception", e.getMessage());
            }
            return handleIndexData(items);
        }
        Callable<Result<List<com.tqw.pojo.Item>>> itemListCallable = () -> itemFeignClient.findItemList();
        Callable<Result<List<ItemCat>>> itemCatListCallable = () -> itemFeignClient.findItemCatList(1);
        FutureTask<Result<List<com.tqw.pojo.Item>>> itemListTask = new FutureTask<>(itemListCallable);
        FutureTask<Result<List<ItemCat>>> itemCatListTask = new FutureTask<>(itemCatListCallable);
        executorService.submit(itemListTask);
        executorService.submit(itemCatListTask);
        Result<List<com.tqw.pojo.Item>> itemList = null;
        Result<List<ItemCat>> itemCatList = null;
        try {
            itemList = itemListTask.get();
            itemCatList = itemCatListTask.get();
        } catch (InterruptedException | ExecutionException e) {
            log.error("com.tqw.es.service.impl.ItemServiceImpl.getItemList {} ", e.getMessage());
        }
        if (itemList == null || itemCatList == null) {
            vo.set("indexCache", null, Duration.ofSeconds(10));
            return null;
        }
        if (itemList.getData()==null||itemList.getData().size() <= 0 || itemCatList.getData()==null||itemCatList.getData().size() <= 0) {
            vo.set("indexCache", null, Duration.ofSeconds(10));
            return null;
        }
        Map<String, List<ItemVO>> map = handleItemList(itemList, itemCatList);
        String json = null;
        try {
            json = objectMapper.writeValueAsString(map);
        } catch (JsonProcessingException e) {
            log.error("com.tqw.es.service.impl.ItemServiceImpl.getItemList {} caused by redis serialization exception", e.getMessage());
        }
        vo.set("indexCache", json);
        return handleIndexData(map);
    }

    /**
     * 将一级分类对应的商品数据按时间排序
     * @param map 一级分类商品数据
     * @return 排序后首页需要的数据
     */
    public List<List<ItemVO>> handleIndexData(Map<String,List<ItemVO>> map){
        List<List<ItemVO>> itemVOList = new ArrayList<>();
        Set<Map.Entry<String, List<ItemVO>>> entries = map.entrySet();
        for (Map.Entry<String, List<ItemVO>> e : entries) {
            List<ItemVO> itemList = new ArrayList<>();
            List<ItemVO> v = e.getValue();
            v.sort((o1, o2) -> {
                if (o1.getCreatedTime().before(o2.getCreatedTime())) {
                    return -1;
                }
                if (o1.getCreatedTime().after(o2.getCreatedTime())) {
                    return 1;
                }
                return 0;
            });
            int count = 0;
            for (ItemVO itemVO : v) {
                itemList.add(itemVO);
                count++;
                if (count>=5) {
                    break;
                }
            }
            itemVOList.add(itemList);
        }
        return itemVOList;
    }

    /**
     * 按一级分类封装所有商品数据
     * @param itemList 商品数据集合
     * @param itemCatList 商品一级分类集合
     * @return 封装的商品数据map
     */
    public Map<String,List<ItemVO>> handleItemList(Result<List<com.tqw.pojo.Item>> itemList, Result<List<ItemCat>> itemCatList){
        List<com.tqw.pojo.Item> items = itemList.getData();
        List<ItemCat> itemCats = itemCatList.getData();
        Map<String,List<ItemVO>> map = new ConcurrentHashMap<>(16);
        for (ItemCat itemCat : itemCats) {
            String key = String.valueOf(itemCat.getId());
            for (com.tqw.pojo.Item item : items) {
                if (item.getGrandfatherCatId().equals(itemCat.getId())) {
                    ItemVO itemVO = new ItemVO();
                    itemVO.setId(item.getId()).setTitle(item.getTitle()).setPrice(item.getPrice()).setFontImage(item.getFontImage()).setItemCatName(itemCat.getName()).setCreatedTime(item.getCreatedTime());
                    if (map.containsKey(key)) {
                        map.get(key).add(itemVO);
                    } else {
                        List<ItemVO> voList = new ArrayList<>();
                        voList.add(itemVO);
                        map.put(key, voList);
                    }
                }
            }
        }
        return map;
    }
}
